/**
 * Copyright (c) 2015-2018, Golly Hu 胡高 (3722711@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.hg.jfsa.web.theme;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.jfinal.aop.Aop;
import com.jfinal.core.JFinal;
import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;
import com.jfinal.plugin.ehcache.CacheKit;
import com.jfinal.plugin.ehcache.IDataLoader;
import com.jfinal.render.Render;
import com.jfinal.render.RenderManager;
import com.jfinal.render.TemplateRender;
import com.jfinal.template.Engine;

import cn.hg.jfsa.core.option.Options;
import cn.hg.jfsa.manage.service.JfsaDictService;
import cn.hg.jfsa.model.JfsaDict;

/**
 * @author Golly
 */
public class ThemeRender extends TemplateRender {

	private static final String contentType = "text/html; charset=" + Render.getEncoding();
	private static String contextPath = JFinal.me().getContextPath();
	private static Engine engine;
	private static Log log = Log.getLog(ThemeRender.class);

	public static String getMatcher (String regex, String source) {
		String result = "";
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(source);

		while (matcher.find()) {
			result = matcher.group(1);
		}

		return result;
	}

	private JfsaDictService dictService = Aop.get(JfsaDictService.class);
	private List<String> excludeViewPostfixList = new ArrayList<>();

	public ThemeRender (String view, List<String> excludeViewPostfixList) {
		super(view);
		this.excludeViewPostfixList = excludeViewPostfixList;
	}

	@Override
	public String getContentType () { return ThemeRender.contentType; }

	/*
	 * (non-Javadoc)
	 * @see com.jfinal.render.Render#render()
	 */
	@Override
	public void render () {

		if (!this.isThemePath()) {
			super.render();
			return;
		}

		this.response.setContentType(this.getContentType());
		Map<Object, Object> data = new HashMap<>();

		for (Enumeration<String> attrs = this.request.getAttributeNames(); attrs.hasMoreElements();) {
			String attrName = attrs.nextElement();
			data.put(attrName, this.request.getAttribute(attrName));
		}

		Theme theme = ThemeManager.me().getCurrentTheme();
		String html = this.getEngine().getTemplate(theme.getWebAbsolutePath() + this.view).renderToString(data);
		html = this.replaceSrcThemeSrcPath(html);

		try {
			this.response.getWriter().write(html);
		}
		catch (IOException e) {
			ThemeRender.log.error("JFSA模板错误", e);
		}

		// TODO:
		// RenderHelpler.actionCacheExec(html, contentType);
		// RenderHelpler.renderHtml(response, html, contentType);
	}

	public String replaceSrcThemeSrcPath (String content) {

		if (StrKit.isBlank(content)) {
			return content;
		}

		Document doc = Jsoup.parse(content);
		// 处理style中的background-image:url元素
		Elements bgImgElements = doc.select("[style*=background-image]");
		this.replaceStyle(bgImgElements);
		List<JfsaDict> list = this.getRenderRoleDictList();

		for (JfsaDict dict : list) {
			Elements elements = doc.select(dict.getPara());
			this.replace(elements, dict.getValue());
		}

		// Elements incElements = doc.select("include[src]");
		// replace(incElements, "src");
		//
		// Elements jsElements = doc.select("script[src]");
		// replace(jsElements, "src");
		//
		// Elements imgElements = doc.select("img[src]");
		// replace(imgElements, "src");
		//
		// Elements linkElements = doc.select("link[href]");
		// replace(linkElements, "href");
		return doc.toString();
	}

	@Override
	public String toString () {
		return this.view;
	}

	private Engine getEngine () {

		if (ThemeRender.engine == null) {
			ThemeRender.engine = RenderManager.me().getEngine();
		}

		return ThemeRender.engine;
	}

	private List<JfsaDict> getRenderRoleDictList () {
		List<JfsaDict> list = CacheKit.get("jfsaDict", "dictList_theme_render_role_type", new IDataLoader() {

			@Override
			public Object load () {
				return ThemeRender.this.dictService.getByParentKey("theme_render_role_type", "on");
			}

		});
		return list;
	}

	private void replace (Elements elements, String attrName) {
		Iterator<Element> iterator = elements.iterator();
		Theme theme = ThemeManager.me().getCurrentTheme();

		while (iterator.hasNext()) {
			Element element = iterator.next();
			String url = element.attr(attrName);

			// 处理src、href中的元素
			if (StrKit.isBlank(url) || url.startsWith("//") || url.toLowerCase().startsWith("http")
				|| url.startsWith("data:image/") || element.hasAttr("cdn-exclude")) {
				continue;
			}

			// 以 / 开头的，需要添加 contextPath
			if (url.startsWith("/")) {

				if (ThemeRender.contextPath.length() > 0 && url.startsWith(ThemeRender.contextPath + "/") == false) {
					url = ThemeRender.contextPath + url;
				}

			}
			// 以 ./ 开头的文件，需要添加模板路径
			else if (url.startsWith("./")) {
				url = ThemeRender.contextPath + theme.getWebAbsolutePath() + url.substring(1);
			}
			// 直接是文件目录名开头
			else {
				url = ThemeRender.contextPath + theme.getWebAbsolutePath() + "/" + url;
			}

			if (Options.getAsBool("cdn_enable")) {
				url = Options.get("cdn_domain") + url;
			}

			element.attr(attrName, url);
		}

	}

	private void replaceStyle (Elements elements) {
		String regex = "(?<=\\()(\\S+)(?=\\))";
		// String regex2 = "(?<=\\')(\\S+)(?=\\')";
		// String regex3 = "(?<=\\\")(\\S+)(?=\\\")";
		Iterator<Element> iterator = elements.iterator();
		Theme theme = ThemeManager.me().getCurrentTheme();

		while (iterator.hasNext()) {
			Element element = iterator.next();
			String style = element.attr("style");
			ThemeRender.log.debug("style属性：" + style);
			String url = ThemeRender.getMatcher(regex, style);

			if (StrKit.isBlank(url)) {
				continue;
			}

			// 去除url两边的引号
			if (url.startsWith("\'") && url.endsWith("\'") || url.startsWith("\"") && url.endsWith("\"")) {
				url = url.substring(1, url.length() - 1);
			}

			ThemeRender.log.debug("style属性中提取的首个URL：" + url);

			// 处理src、href中的元素
			if (StrKit.isBlank(url) || url.startsWith("//") || url.toLowerCase().startsWith("http")
				|| url.startsWith("data:image/") || element.hasAttr("cdn-exclude")) {
				continue;
			}

			String orgUrl = new String(url);

			// 以 / 开头的，需要添加 contextPath
			if (url.startsWith("/")) {

				if (ThemeRender.contextPath.length() > 0 && url.startsWith(ThemeRender.contextPath + "/") == false) {
					url = ThemeRender.contextPath + url;
				}

			}
			// 以 ./ 开头的文件，需要添加模板路径
			else if (url.startsWith("./")) {
				url = ThemeRender.contextPath + theme.getWebAbsolutePath() + url.substring(1);
			}
			// 直接是文件目录名开头
			else {
				url = ThemeRender.contextPath + theme.getWebAbsolutePath() + "/" + url;
			}

			if (Options.getAsBool("cdn_enable")) {
				url = Options.get("cdn_domain") + url;
			}

			ThemeRender.log.debug("处理过的URL：" + url);
			style = style.replace(orgUrl, url);
			ThemeRender.log.debug("处理过的Style：" + style);
			element.attr("style", style);
		}

	}

	protected boolean isThemePath () {
		String target = this.request.getRequestURI();

		if (target.startsWith(ThemeRender.contextPath)) {
			target = target.substring(ThemeRender.contextPath.length());
		}

		for (String prefix : this.excludeViewPostfixList) {

			if (target.startsWith(prefix)) {
				return false;
			}

		}

		return true;
	}

}
